Imports System
Imports System.Text
Imports System.Data
Imports System.Collections.Generic

Imports Framework
Imports System.Net.Mail



'Table-Row Class (Customisable half)
Partial Public Class COrder

#Region "Constants"
    'Join Expressions

    Public Const VIEW_UNIQUE As String = "vwMcm_UniqueOrders"
#End Region

#Region "Constructors (Public)"
    'Default Connection String
    Public Sub New()
        MyBase.New()
    End Sub
    Public Sub New([orderId] As Integer)
        MyBase.New([orderId])
    End Sub

    'Alternative Connection String
    Public Sub New(ByVal dataSrc As CDataSrc)
        MyBase.New(dataSrc)
    End Sub
    Public Sub New(ByVal dataSrc As CDataSrc, [orderId] As Integer)
        MyBase.New(dataSrc, [orderId])
    End Sub

    'Transactional (shares an open connection)
    Protected Friend Sub New(ByVal dataSrc As CDataSrc, ByVal [orderId] As Integer, ByVal txOrNull As IDbTransaction)
        MyBase.New(dataSrc, [orderId], txOrNull)
    End Sub
#End Region

#Region "Default Values"
    Protected Overrides Sub InitValues_Custom()
        'Custom default values (e.g. DateCreated column)
        m_orderCreated = DateTime.Now

        'Member variables (e.g. for child collections)
        m_payments = New CPaymentList()
    End Sub
#End Region

#Region "Default Connection String"
    Protected Overrides Function DefaultDataSrc() As CDataSrc
        Return CDataSrc.Default
    End Function
#End Region

#Region "Members"
    'Foreign Keys   
    <NonSerialized()> Private m_Customer As CCustomer

    'Child Collections  
    <NonSerialized()> Private m_payments As CPaymentList

    'Xml Data (as high-level objects)

#End Region

#Region "Properties - Relationships"
    'Relationships - Foriegn Keys (e.g parent)
    Public Property [Product]() As CProduct
        Get
            Return CProduct.Cache.GetById(Me.OrderProductId)
        End Get
        Set(value As CProduct)
            m_orderProductId = value.ProductId
            m_orderPriceHistorical = value.ProductPrice
            m_productName = value.ProductName
        End Set
    End Property
    Public Property [Customer]() As CCustomer
        Get
            If IsNothing(m_Customer) Then
                SyncLock (Me)
                    If IsNothing(m_Customer) Then
                        m_Customer = New CCustomer(Me.OrderCustomerId)
                    End If
                End SyncLock
            End If
            Return m_Customer
        End Get
        Set(Value As CCustomer)
            m_Customer = Value
            If Not IsNothing(Value) Then
                m_orderCustomerId = Value.CustomerId
                m_customerEmail = Value.CustomerEmail
                m_customerName = Value.CustomerName
            Else
                m_orderCustomerId = Integer.MinValue
            End If
        End Set
    End Property

    Public Property [Payments]() As CPaymentList
        Get
            If IsNothing(m_payments) Then
                SyncLock (Me)
                    If IsNothing(m_payments) Then
                        m_payments = New CPayment(DataSrc).SelectByOrderId(Me.OrderId)
                        m_payments.Order = Me
                    End If
                End SyncLock
            End If
            Return m_payments
        End Get
        Set(Value As CPaymentList)
            m_payments = Value
            If Not IsNothing(Value) Then
                m_payments.Order = Me
            End If
        End Set
    End Property
    Friend Function Payments_(ByVal tx As IDbTransaction) As CPaymentList
        With New CPayment(DataSrc)
            Return .SelectByOrderId(Me.OrderId, tx)     'Only used for cascade deletes
        End With
    End Function
    Public Function PaymentsCount() As Integer
        If Not IsNothing(m_payments) Then Return m_payments.Count
        With New CPayment(DataSrc)
            Return .SelectCountByOrderId(Me.OrderId)
        End With
    End Function
#End Region

#Region "Properties - Customisation"
    'Derived/ReadOnly (e.g. xml classes, presentation logic)
#End Region

#Region "Save/Delete Overrides"
    Public Overrides Sub Delete(ByVal txOrNull As IDbTransaction)
        'Use a transaction if none supplied
        If txOrNull Is Nothing Then
            BulkDelete(Me)
            Exit Sub
        End If

        'Cascade-Update/delete (all child collections)
        For Each i As CPayment In Me.Payments_(txOrNull)
            i.PaymentOrderId = Integer.MinValue
            i.Save(txOrNull)
        Next

        'Normal Delete
        MyBase.Delete(txOrNull)
    End Sub
#End Region

#Region "Custom Database Queries"
    Public Function SelectByCustomerIds(ByVal CustomerIds As List(Of Integer)) As COrderList
        Return SelectWhere(New CCriteriaList("OrderCustomerId", ESign.IN, CustomerIds))
    End Function
    Public Function SalesPaidForProduct(ByVal productId As Integer) As Decimal
        Dim where As New CCriteriaList(2)
        where.Add("OrderProductId", productId)
        where.Add("OrderIsPaid", True)
        Return SelectSum("OrderPriceHistorical", Nothing, where)
    End Function
    Public Function SalesValueForProduct(ByVal productId As Integer) As Decimal
        Return SelectSum("OrderPriceHistorical", Nothing, New CCriteriaList("OrderProductId", productId))
    End Function
    Public Function SalesPaidForCustomer(ByVal customerId As Integer) As Decimal
        Dim where As New CCriteriaList(2)
        where.Add("OrderCustomerId", customerId)
        where.Add("OrderIsPaid", True)
        Return SelectSum("OrderPriceHistorical", Nothing, where)
    End Function
#End Region

#Region "Searching (Optional)"
    'Dynamic search methods: (overload as required for common search patterns, cascade the BuildWhere overloads)
    '   Public  x5 - Simple, Paged, Transactional, Count, and Dataset
    '   Private x1 - BuildWhere
    'See also in-memory search options in list class, such as GetBy[FK] and Search

    'Simple
    Public Function SelectSearch(ByVal nameOrId As String, ByVal productId As Integer, ByVal CustomerId As Integer, ByVal isPaid As Boolean?) As COrderList
        Return SelectWhere(BuildWhere(nameOrId, productId, CustomerId, isPaid)) ', JOIN_OR_VIEW)
    End Function

    'Paged
    Public Function SelectSearch(ByVal pi As CPagingInfo, ByVal nameOrId As String, ByVal productId As Integer, ByVal CustomerId As Integer, ByVal isPaid As Boolean?) As COrderList
        'pi.TableName = JOIN_OR_VIEW
        Return SelectWhere(pi, BuildWhere(nameOrId, productId, CustomerId, isPaid))
    End Function

    'Transactional
    Public Function SelectSearch(ByVal nameOrId As String, ByVal productId As Integer, ByVal CustomerId As Integer, ByVal isPaid As Boolean?, ByVal tx As IDbTransaction) As COrderList
        Return SelectWhere(BuildWhere(nameOrId, productId, CustomerId, isPaid), tx) ', JOIN_OR_VIEW, tx)
    End Function

    'Count
    Public Overloads Function SelectCount(ByVal nameOrId As String, ByVal productId As Integer, ByVal CustomerId As Integer, ByVal isPaid As Boolean?) As Integer
        Return SelectCount(BuildWhere(nameOrId, productId, CustomerId, isPaid)) ', JOIN_OR_VIEW)
    End Function

    'Dataset (e.g. ExportToCsv)
    Public Function SelectSearch_Dataset(ByVal nameOrId As String, ByVal productId As Integer, ByVal CustomerId As Integer, ByVal isPaid As Boolean?) As DataSet
        Return SelectWhere_Dataset(BuildWhere(nameOrId, productId, CustomerId, isPaid)) ', JOIN_OR_VIEW)
    End Function

    'Totals
    Public Function SelectTotalValue(ByVal nameOrId As String, ByVal productId As Integer, ByVal CustomerId As Integer) As Decimal
        If Not String.IsNullOrEmpty(nameOrId) Then
            Return SelectSum("OrderPriceHistorical", Nothing, BuildWhere(nameOrId, productId, CustomerId, Nothing))
        Else
            Return SelectSum("OrderPriceHistorical", VIEW_UNIQUE, BuildWhere(nameOrId, productId, CustomerId, Nothing))
        End If
    End Function
    Public Function SelectTotalPaid(ByVal nameOrId As String, ByVal productId As Integer, ByVal CustomerId As Integer) As Decimal
        Return SelectSum("PaidAmount", Nothing, BuildWhere(nameOrId, productId, CustomerId, Nothing))
    End Function
    Public Function SelectTotalNet(ByVal nameOrId As String, ByVal productId As Integer, ByVal CustomerId As Integer) As Decimal
        Return SelectSum("PaidNet", Nothing, BuildWhere(nameOrId, productId, CustomerId, Nothing))
    End Function



    'Filter Logic
    'Represents a simple search box to search PK and any string columns
    Private Function BuildWhere(ByVal nameOrId As String) As CCriteriaList
        Dim where As New CCriteriaList 'Defaults to AND logic

        'Simple search box UI
        If Not String.IsNullOrEmpty(nameOrId) Then
            'Interpret search string in various ways using OR sub-expression
            Dim orExpr As New CCriteriaGroup(EBoolOperator.Or)

            'Special case - search by PK (assumes integer PK)
            Dim id As Integer
            If Integer.TryParse(nameOrId, id) Then
                orExpr.Add("OrderId", id)
            End If

            'Search a range of string columns
            Dim wildCards As String = String.Concat("%", nameOrId, "%")
            orExpr.Add("ProductName", ESign.Like, wildCards)
            orExpr.Add("CustomerName", ESign.Like, wildCards)
            orExpr.Add("CustomerEmail", ESign.Like, wildCards)

            'Conclude
            If orExpr.Group.Count > 0 Then
                where.Add(orExpr)
            End If
        End If

        Return where
    End Function
    'Represents more complex combinations of search filters (suggestion only)
    Private Function BuildWhere(ByVal nameOrId As String, ByVal productId As Integer, ByVal CustomerId As Integer, ByVal isPaid As Boolean?) As CCriteriaList
        Dim where As CCriteriaList = BuildWhere(nameOrId) 'Reuses logic above

        'Other search Colums (customise as required)
        If Integer.MinValue <> productId Then where.Add("OrderProductId", productId)
        If Integer.MinValue <> CustomerId Then where.Add("OrderCustomerId", CustomerId)
        If isPaid.HasValue Then where.Add("OrderIsPaid", isPaid) 'Customise bool filters according to UI (e.g. for checkbox, use simple bool and bias in one direction)

        Return where
    End Function
#End Region

#Region "Cloning"
    Public Function Clone(target as CDataSrc, txOrNull as IDbTransaction) As COrder ', parentId As Integer) As COrder
        'Shallow copy: Copies the immediate record, excluding autogenerated Pks
        Dim copy As New COrder(Me, target)

        'Deep Copy - Child Entities: Cloned children must reference their cloned parent
        'copy.SampleParentId = parentId

        copy.Save(txOrNull)

        'Deep Copy - Parent Entities: Cloned parents also clone their child collections
        'Me.Children.Clone(target, txOrNull, copy.OrderId)

        Return copy
    End Function
#End Region

#Region "ToXml"
    Protected Overrides Sub ToXml_Custom(ByVal w As System.Xml.XmlWriter)
        'Store(w, "Example", Me.Example)
    End Sub
#End Region


    Public Shared Function Create(productId As Integer, email As String, name As String, news As Boolean) As COrder
        'Validate email, guess name
        If String.IsNullOrEmpty(email) OrElse Not email.Contains("@") Then Throw New Exception("Create Order: Bad email address '" & email & "'")
        email = email.Trim.ToLower
        If String.IsNullOrEmpty(name) Then
            name = email.Substring(0, email.IndexOf("@"))
        End If

        'Get the product
        Dim p As CProduct = CProduct.Cache.GetById(productId)
        If IsNothing(p) Then Throw New Exception("Create Order: Bad productId " & productId)

        'Get Customer id
        Dim c As CCustomer = New CCustomer().SelectByEmail(email)
        If IsNothing(c) Then
            c = New CCustomer
            c.CustomerEmail = email
            c.CustomerName = name
            c.CustomerSendMeNewsletter = news
            c.Save()
        ElseIf Not String.IsNullOrEmpty(name) AndAlso c.CustomerName <> name Then
            c.CustomerName = name
            c.Save()
        End If

        'Create the order
        Dim o As New COrder()
        o.Customer = c
        o.Product = p
        o.Save()

        'Get the email template
        Dim t As CEmailTemplate = CEmailTemplate.Get(EEmailTemplate.Download)

        'Send the mail
        Dim msg As New MailMessage()
        Try
            With msg
                .IsBodyHtml = True
                .To.Add(New MailAddress(email, name))
                .Subject = t.ProcessSubject(o.Product, o.Customer)
                .Body = t.Process(o, msg, Nothing)

                '*Sender is downloads.mikeconnellminstries.com (not gmail)
                .ReplyToList.Add(New MailAddress("mikeconnellministries@gmail.com", "Mike Connell Ministries"))
            End With

            With New SmtpClient
                .Send(msg)
            End With
            'CConfig.Gmail.Send(msg)
            msg.Dispose()
        Catch ex As Exception
            SchemaAudit.CAudit_Error.Log(ex, email, name)
        End Try

        Return o
    End Function

End Class
